#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/slogcodes.h>
#include <string.h>
#include <errno.h>
#include <stdarg.h>
#include <vcapture/capture.h>

#include "capture_platform.h"

#define CAPTURE_LOG_NDEV_MAX (16)

#define CAPTURE_LOG_NOT_INITIALIZED	0
#define CAPTURE_LOG_INITIALIZED		1

typedef struct _log_context {
	uint32_t inuse;
	captrure_device_type_t type;
	uint32_t verbose;
	char *name;
} log_context_t;

typedef struct _prop_str {
	uint32_t id;
	const char * const pstr;
} prop_str_t;

static log_context_t log_context[CAPTURE_LOG_NDEV_MAX];
static int init_flag = CAPTURE_LOG_NOT_INITIALIZED;
static prop_str_t prop_str[] = {
	{ CAPTURE_PROPERTY('I','X','N','V'), "CAPTURE_PROPERTY_IMX8X_NUM_EARLY_VSYNC_IGNORED" },
	{ CAPTURE_PROPERTY('I','X','S','F'), "CAPTURE_PROPERTY_IMX8X_SOURCE_FLAGS" },
	{ CAPTURE_PROPERTY('I','X','X','E'), "CAPTURE_PROPERTY_IMX8X_XRDC_ENABLE" },
	{ CAPTURE_PROPERTY('I','X','R','F'), "CAPTURE_PROPERTY_IMX8X_RESET_FLAGS" },
	{ CAPTURE_PROPERTY('I','X','P','C'), "CAPTURE_PROPERTY_IMX8X_CIPI_CCIR_EN" },
	{ CAPTURE_PROPERTY('I','X','P','V'), "CAPTURE_PROPERTY_IMX8X_CIPI_CCIR_EXT_VSYNC_EN" },
	{ CAPTURE_PROPERTY('I','X','P','W'), "CAPTURE_PROPERTY_IMX8X_CIPI_PIXEL_WIDTH" },
	{ CAPTURE_PROPERTY('I','X','P','H'), "CAPTURE_PROPERTY_IMX8X_CIPI_HSYNC_WIDTH" },
	{ CAPTURE_PROPERTY('I','X','P','S'), "CAPTURE_PROPERTY_IMX8X_CIPI_VSYNC_WIDTH" },
	{ CAPTURE_PROPERTY('I','X','P','M'), "CAPTURE_PROPERTY_IMX8X_CIPI_MASK_VSYNC_CNT" },
	{ CAPTURE_PROPERTY('I','X','P','G'), "CAPTURE_PROPERTY_IMX8X_CIPI_GCLK_MODE_EN" },
	{ CAPTURE_PROPERTY('I','X','P','U'), "CAPTURE_PROPERTY_IMX8X_CIPI_UV_SWAP_EN" },
	{ CAPTURE_PROPERTY('I','X','C','H'), "CAPTURE_PROPERTY_IMX8X_CSI2_HS_SETTLE" },
	{ CAPTURE_PROPERTY('Q','R','D','P'), "CAPTURE_PROPERTY_CSI2_RESET_D_PHY" },
	{ CAPTURE_PROPERTY('Q','C','P','R'), "CAPTURE_PROPERTY_CAMERA_POWER_RESET" },
	{ CAPTURE_PROPERTY('Q','C','A','S'), "CAPTURE_PROPERTY_CAMERA_STATUS" },
	{ CAPTURE_PROPERTY('Q','R','D','P'), "CAPTURE_PROPERTY_CSI2_RESET_D_PHY" },
	{ CAPTURE_PROPERTY('Q','C','P','R'), "CAPTURE_PROPERTY_CAMERA_POWER_RESET" },
	{ CAPTURE_PROPERTY('Q','C','A','S'), "CAPTURE_PROPERTY_CAMERA_STATUS" },
	{ CAPTURE_PROPERTY('Q','D','E','C'), "CAPTURE_PROPERTY_DECODER" },
	{ CAPTURE_PROPERTY('Q','C','A','M'), "CAPTURE_PROPERTY_CAMERA" },
	{ CAPTURE_PROPERTY('Q','C','A','U'), "CAPTURE_PROPERTY_CAMERA_USE" },
	{ CAPTURE_PROPERTY('Q','I','N','F'), "CAPTURE_PROPERTY_DEVICE_INFO" },
	{ CAPTURE_PROPERTY('Q','E','N','A'), "CAPTURE_ENABLE" },
	{ CAPTURE_PROPERTY('Q','N','D','V'), "CAPTURE_PROPERTY_NDEVICES" },
	{ CAPTURE_PROPERTY('Q','D','E','V'), "CAPTURE_PROPERTY_DEVICE" },
	{ CAPTURE_PROPERTY('Q','N','S','R'), "CAPTURE_PROPERTY_NSOURCES" },
	{ CAPTURE_PROPERTY('Q','C','O','N'), "CAPTURE_PROPERTY_CONTRAST" },
	{ CAPTURE_PROPERTY('Q','B','R','I'), "CAPTURE_PROPERTY_BRIGHTNESS" },
	{ CAPTURE_PROPERTY('Q','S','A','T'), "CAPTURE_PROPERTY_SATURATION" },
	{ CAPTURE_PROPERTY('Q','H','U','E'), "CAPTURE_PROPERTY_HUE" },
	{ CAPTURE_PROPERTY('Q','S','R','P'), "CAPTURE_PROPERTY_SHARPNESS" },
	{ CAPTURE_PROPERTY('Q','D','E','I'), "CAPTURE_PROPERTY_DEINTERLACE_FLAGS" },
	{ CAPTURE_PROPERTY('Q','D','E','M'), "CAPTURE_PROPERTY_DEINTERLACE_MODE" },
	{ CAPTURE_PROPERTY('Q','N','O','S'), "CAPTURE_PROPERTY_NO_SIGNAL_MODE" },
	{ CAPTURE_PROPERTY('Q','N','C','D'), "CAPTURE_PROPERTY_DST_COLOR_SPACE" },
	{ CAPTURE_PROPERTY('Q','N','C','S'), "CAPTURE_PROPERTY_SRC_COLOR_SPACE" },
	{ CAPTURE_PROPERTY('Q','M','N','B'), "CAPTURE_PROPERTY_MIN_NBUFFERS" },
	{ CAPTURE_PROPERTY('Q','B','U','S'), "CAPTURE_PROPERTY_BUFFER_USAGE" },
	{ CAPTURE_PROPERTY('Q','B','I','X'), "CAPTURE_PROPERTY_BUFFER_INDEX" },
	{ CAPTURE_PROPERTY('Q','B','N','F'), "CAPTURE_PROPERTY_BUFFER_NFIELDS" },
	{ CAPTURE_PROPERTY('Q','B','P','O'), "CAPTURE_PROPERTY_BUFFER_PLANAR_OFFSETS" },
	{ CAPTURE_PROPERTY('Q','B','F','L'), "CAPTURE_PROPERTY_BUFFER_FLAGS" },
	{ CAPTURE_PROPERTY('Q','B','S','N'), "CAPTURE_PROPERTY_BUFFER_SEQNO" },
	{ CAPTURE_PROPERTY('Q','N','O','R'), "CAPTURE_PROPERTY_NORM" },
	{ CAPTURE_PROPERTY('Q','U','B','L'), "CAPTURE_PROPERTY_UNBLOCK" },
	{ CAPTURE_PROPERTY('Q','Q','N','M'), "CAPTURE_PROPERTY_CURRENT_NORM" },
	{ CAPTURE_PROPERTY('Q','L','F','I'), "CAPTURE_PROPERTY_INVERT_FID_POL" },
	{ CAPTURE_PROPERTY('Q','L','H','S'), "CAPTURE_PROPERTY_INVERT_VSYNC_POL" },
	{ CAPTURE_PROPERTY('Q','L','V','S'), "CAPTURE_PROPERTY_INVERT_HSYNC_POL" },
	{ CAPTURE_PROPERTY('Q','L','P','C'), "CAPTURE_PROPERTY_INVERT_CLOCK_POL" },
	{ CAPTURE_PROPERTY('Q','L','D','E'), "CAPTURE_PROPERTY_INVERT_DATAEN_POL" },
	{ CAPTURE_PROPERTY('Q','L','D','A'), "CAPTURE_PROPERTY_INVERT_DATA_POL" },
	{ CAPTURE_PROPERTY('Q','S','F','O'), "CAPTURE_PROPERTY_SRC_FORMAT" },
	{ CAPTURE_PROPERTY('Q','S','F','S'), "CAPTURE_PROPERTY_SRC_STRIDE" },
	{ CAPTURE_PROPERTY('Q','S','W','I'), "CAPTURE_PROPERTY_SRC_WIDTH" },
	{ CAPTURE_PROPERTY('Q','S','H','E'), "CAPTURE_PROPERTY_SRC_HEIGHT" },
	{ CAPTURE_PROPERTY('Q','C','W','I'), "CAPTURE_PROPERTY_CROP_WIDTH" },
	{ CAPTURE_PROPERTY('Q','C','H','E'), "CAPTURE_PROPERTY_CROP_HEIGHT" },
	{ CAPTURE_PROPERTY('Q','S','I','D'), "CAPTURE_PROPERTY_SRC_INDEX" },
	{ CAPTURE_PROPERTY('Q','C','X','P'), "CAPTURE_PROPERTY_CROP_X" },
	{ CAPTURE_PROPERTY('Q','C','Y','P'), "CAPTURE_PROPERTY_CROP_Y" },
	{ CAPTURE_PROPERTY('Q','D','F','W'), "CAPTURE_PROPERTY_DST_WIDTH" },
	{ CAPTURE_PROPERTY('Q','D','F','H'), "CAPTURE_PROPERTY_DST_HEIGHT" },
	{ CAPTURE_PROPERTY('Q','D','F','S'), "CAPTURE_PROPERTY_DST_STRIDE" },
	{ CAPTURE_PROPERTY('Q','D','F','B'), "CAPTURE_PROPERTY_DST_NBYTES" },
	{ CAPTURE_PROPERTY('Q','F','B','N'), "CAPTURE_PROPERTY_FRAME_NBUFFERS" },
	{ CAPTURE_PROPERTY('Q','F','B','A'), "CAPTURE_PROPERTY_FRAME_BUFFERS" },
	{ CAPTURE_PROPERTY('Q','F','B','T'), "CAPTURE_PROPERTY_FRAME_TIMESTAMP" },
	{ CAPTURE_PROPERTY('Q','F','B','C'), "CAPTURE_PROPERTY_FRAME_TIMECODE" },
	{ CAPTURE_PROPERTY('Q','F','B','S'), "CAPTURE_PROPERTY_FRAME_SEQNO" },
	{ CAPTURE_PROPERTY('Q','F','B','F'), "CAPTURE_PROPERTY_FRAME_FLAGS" },
	{ CAPTURE_PROPERTY('Q','F','B','B'), "CAPTURE_PROPERTY_FRAME_NBYTES" },
	{ CAPTURE_PROPERTY('Q','D','F','F'), "CAPTURE_PROPERTY_DST_FORMAT" },
	{ CAPTURE_PROPERTY('Q','P','L','O'), "CAPTURE_PROPERTY_PLANAR_OFFSETS" },
	{ CAPTURE_PROPERTY('Q','T','P','R'), "CAPTURE_PROPERTY_THREAD_PRIORITY" },
	{ CAPTURE_PROPERTY('Q','P','I','F'), "CAPTURE_PROPERTY_INTERFACE_TYPE" },
	{ CAPTURE_PROPERTY('Q','D','B','W'), "CAPTURE_PROPERTY_DATA_BUS_WIDTH" },
	{ CAPTURE_PROPERTY('Q','C','N','D'), "CAPTURE_PROPERTY_CSI2_NUM_DATA_LANES" },
	{ CAPTURE_PROPERTY('Q','C','C','P'), "CAPTURE_PROPERTY_CSI2_CLK_LANE_POS" },
	{ CAPTURE_PROPERTY('Q','C','D','0'), "CAPTURE_PROPERTY_CSI2_DATA0_LANE_POS" },
	{ CAPTURE_PROPERTY('Q','C','D','1'), "CAPTURE_PROPERTY_CSI2_DATA1_LANE_POS" },
	{ CAPTURE_PROPERTY('Q','C','D','2'), "CAPTURE_PROPERTY_CSI2_DATA2_LANE_POS" },
	{ CAPTURE_PROPERTY('Q','C','D','3'), "CAPTURE_PROPERTY_CSI2_DATA3_LANE_POS" },
	{ CAPTURE_PROPERTY('Q','C','V','N'), "CAPTURE_PROPERTY_CSI2_VCHANNEL_NUM" },
	{ CAPTURE_PROPERTY('Q','C','D','C'), "CAPTURE_PROPERTY_CSI2_DDR_CLOCK_MHZ" },
	{ CAPTURE_PROPERTY('Q','V','B','R'), "CAPTURE_PROPERTY_VERBOSITY" },
	{ CAPTURE_PROPERTY('Q','V','B','F'), "CAPTURE_PROPERTY_VERBOSITY_FILE" },
	{ CAPTURE_PROPERTY('Q','L','M','P'), "CAPTURE_PROPERTY_LOGMSG_PREFIX" },
	{ CAPTURE_PROPERTY('Q','D','B','G'), "CAPTURE_PROPERTY_DEBUG_ENABLE" },
	{ CAPTURE_PROPERTY('Q','D','I','P'), "CAPTURE_PROPERTY_DECODER_I2C_PATH" },
	{ CAPTURE_PROPERTY('Q','D','I','A'), "CAPTURE_PROPERTY_DECODER_I2C_ADDR" },
	{ CAPTURE_PROPERTY('Q','D','I','S'), "CAPTURE_PROPERTY_DECODER_I2C_SPEED" },
	{ CAPTURE_PROPERTY('Q','U','D','P'), "CAPTURE_PROPERTY_USB_DEVICE_PATH" },
	{ CAPTURE_PROPERTY('Q','D','E','E'), "CAPTURE_PROPERTY_DECODER_ENABLE" },
};

void capture_log_init(void)
{
	int i;

	if (init_flag == CAPTURE_LOG_NOT_INITIALIZED) {
		for (i = 0; i < CAPTURE_LOG_NDEV_MAX; i++) {
			log_context[i].name = NULL;
			log_context[i].verbose = 0;
			log_context[i].type = CAPTURE_DEV_TYPE_UNKNOWN;
			log_context[i].inuse = 0;
		}
		init_flag = CAPTURE_LOG_INITIALIZED;
	}
}

capture_log_context_t capture_log_create_context(captrure_device_type_t type, const char *name)
{
	int i;
	int size;

	if ((type == CAPTURE_DEV_TYPE_UNKNOWN) || !name)
		goto error_param;

	for (i = 0; i < CAPTURE_LOG_NDEV_MAX; i++) {
		if (log_context[i].inuse == 0)
			break;
	}

	if (i >= CAPTURE_LOG_NDEV_MAX)
		goto error_full;

	size = strlen(name);
	log_context[i].name = (char *)malloc(size + 1);
	if (!log_context[i].name) {
		goto error_mem;
	}
	memcpy(log_context[i].name, name, size);
	log_context[i].name[size] = '\0';

	log_context[i].verbose = 0;
	log_context[i].type = type;
	log_context[i].inuse = 1;

	return i;

error_param:
error_mem:
error_full:
	return -1;
}

void capture_log_destroy_context(capture_log_context_t context)
{
	if (context < 0 || context >= CAPTURE_LOG_NDEV_MAX)
		return;
	if (log_context[context].name) {
		free(log_context[context].name);
		log_context[context].name = NULL;
	}
	log_context[context].verbose = 0;
	log_context[context].type = CAPTURE_DEV_TYPE_UNKNOWN;
	log_context[context].inuse = 0;
}

void capture_log_set_verbose(capture_log_context_t context, uint32_t verbose)
{
	if (context < 0 || context >= CAPTURE_LOG_NDEV_MAX || log_context[context].inuse != 1)
		return;
	log_context[context].verbose = verbose;
}

void capture_log(capture_log_context_t context, int level, int severity, const char *fmt, ...)
{
	char *type_str = NULL;

	if (context < 0 || context >= CAPTURE_LOG_NDEV_MAX || log_context[context].inuse != 1)
		return;

	switch (log_context[context].type) {
	case CAPTURE_DEV_TYPE_SOC: // reserved
		type_str = "soc";
		break;
	case CAPTURE_DEV_TYPE_DECODER_CAMERA:
		type_str = "decoder_camera";
		break;
	case CAPTURE_DEV_TYPE_BOARD:
		type_str = "board";
		break;
	default:
		return;
	}

	int err = errno;
	va_list	vargs;
	char fmt2[strlen(type_str) + sizeof("(") + strlen(log_context[context].name) + sizeof(") ") + strlen(fmt)];

	sprintf(fmt2, "%s(%s) %s", type_str, log_context[context].name, fmt);

	va_start(vargs, fmt);
	vslogf(_SLOG_SETCODE(_SLOGC_CAMERA, 0), severity, fmt2, vargs);
	va_end(vargs);

	errno = err;
}

const char *capture_log_prop_str(uint32_t id)
{
	int i;

	for (i = 0; i < sizeof(prop_str) / sizeof(prop_str[0]); i++) {
		if (prop_str[i].id == id)
			return prop_str[i].pstr;
	}

	return "";
}
